home *** CD-ROM | disk | FTP | other *** search
/ Nikkei Mac 20 / NIKKEI-MAC-CD-VOL-20-1998-12.ISO.7z / NIKKEI-MAC-CD-VOL-20-1998-12.ISO / オンラインソフト / 0.日経MAC超定番 (1) / DarkSide504L.sit / DarkSide of the Mac 5.0.4 / SampleFaders / Morpion.c < prev    next >
Text File  |  1998-10-20  |  10KB  |  338 lines

  1. /*
  2.     Morpion (French) == 5 In A Row (English)
  3.     The first one who puts 5 markers in a row (horizontal, vertical or diagonal) wins. The computer plays against itself.
  4.     
  5.     This small fader is intended as a code example. It is based on FaderShell, written by Tom Dowdy.
  6.     This code is hereby placed into the public domain. Use it as a template to write your faders!
  7.         
  8.     You can use global variables, they are stored as an offset from A4 using THINK's SetupA4/RestoreA4 procedures.
  9.     
  10.     This fader also demonstrates the use of callback routines by playing sound. Note that in order to play sound, you must
  11.     request a sound channel from DarkSide by creating a 'Chnl' 0 resource. 
  12. */
  13.  
  14. #include <Memory.h>
  15. #include <Windows.h>
  16. #include <Dialogs.h>
  17. #include <Errors.h>
  18. #include <ToolUtils.h>
  19.  
  20. #include "Fader.h"                                // include DarkSide's interface
  21.  
  22. #ifdef THINK_C
  23.     #define QUICKDRAWBLACK    (qd.black)
  24.     #define QUICKDRAWWHITE    (qd.white)
  25. #else
  26.     #define QUICKDRAWBLACK    (&qd.black)
  27.     #define QUICKDRAWWHITE    (&qd.white)
  28. #endif
  29.  
  30. // Constants. These are specific to Morpion, you can throw them away if you are writing a new fader.
  31.  
  32. #define    border        2
  33.  
  34. #define    empty        0
  35. #define    player1        1
  36. #define    player2        2
  37.  
  38. // Global variables, referenced using A4. Also Morpion-specific.
  39. QDGlobals    qd;
  40.  
  41. short        xloc, yloc;                        // starting location of the grid
  42. short         xsize, ysize;                        // Width and height of grid
  43. short        xc, yc;                            // Pixel coordinates of topleft corner
  44. Handle        grid, points, possible;    
  45. short        value[5][5];
  46. short        turn;                                // Tells who's up
  47. Boolean        draw, winner;                        // End-of-game flags
  48. short        unit;                                // Size of squares
  49. long        nextDraw;                        // TickCount of the next time to draw
  50.  
  51. // grid is considered as a two dimensional array 0..xsize-1, 0..ysize-1 of chars
  52. // points as an array 0..xsize-1, 0..ysize-1 of shorts
  53. // possible as an array 0..xsize*ysize-1 of points
  54.  
  55. #define    Grid(x, y)        ((char*) (*grid)) [xsize * y + x]
  56. #define    Points(x, y)    ((short*) (*points)) [xsize * y + x]
  57. #define    Possible(x)    ((Point*) (*possible)) [x]
  58.  
  59. void StartGame (void)
  60. {
  61.     long        ix;
  62.     char         *p;
  63.  
  64.     turn = player1;                                                // white plays first
  65.     draw = winner = false;                                        
  66.     for (p = *grid, ix = xsize*ysize; ix; p++, ix--) *p = empty;            // clear the grid
  67. }
  68.  
  69. // Called when fader is starting up, before window has been created.
  70. // We initialize our global variables here.
  71.  
  72. OSErr    PreflightFader(MachineInfoPtr machineInfo, long *minTicks, long *maxTicks)
  73. {    
  74.     Rect        bounds;
  75.     long        gridsize;
  76.     short    a, b;
  77.     
  78.     *minTicks = 1;                                    // Tell DarkSide how often we want to be called.
  79.     *maxTicks = 15;
  80.     
  81.     BlockMove(machineInfo->applicationQD, &qd, sizeof(qd));
  82.     
  83.     // The rest is specific to Morpion.
  84.     
  85.     unit = machineInfo->faderSettings->theShorts[1];                    // read the settings
  86.     bounds = machineInfo->theScreens[0].bounds;                        // compute the size of our grid
  87.     xloc = bounds.left;
  88.     yloc = bounds.top;
  89.     xsize = (bounds.right - bounds.left - 6 * unit) / unit;
  90.     ysize = (bounds.bottom - bounds.top - 6 * unit) / unit;
  91.     xc = bounds.left + (bounds.right - bounds.left - unit * xsize) / 2;
  92.     yc = bounds.top + (bounds.bottom - bounds.top - unit * ysize) / 2;
  93.     
  94.     grid = BestNewHandle (gridsize = xsize * ysize);
  95.     if (!(grid))                // allocate memory for the grid
  96.         return memFullErr;
  97.     possible = BestNewHandle (4*gridsize);
  98.     if (!(possible)) {                        // allocate memory for the 'possible' array
  99.         DisposHandle(grid);
  100.         return memFullErr;
  101.     }
  102.     points = BestNewHandle (2*gridsize);
  103.     if (!(points))    {                    // allocate memory for the 'points' array
  104.         DisposHandle(possible);
  105.         DisposHandle(grid);
  106.         return memFullErr;
  107.     }
  108.     
  109.     for (a = 0; a < 5; a++)                                        // initialize the 'value' array
  110.         for (b = 0; b < 5; b++)
  111.             value [a][b] = 0;
  112.     value [0][0] = 10; value [0][1] = 20; value [0][2] = 80; value [0][3] = 300; value [0][4] = 2000;
  113.     value [1][0] = 20; value [2][0] = 70; value [3][0] = 400; value [4][0] = 4000;
  114.     
  115.     StartGame();
  116.  
  117.     return noErr;
  118. }
  119.  
  120. // Called when fader is starting up, after window has been created. We usually erase the screens here.
  121. // We then draw the grid.
  122.  
  123. OSErr    InitializeFader(MachineInfoPtr machineInfo)
  124. {
  125.     short        screenIndex;
  126.     short        ix;
  127.  
  128.     // store the local coordinates of our windows
  129.     xloc = machineInfo->theScreens[0].bounds.left;
  130.     yloc = machineInfo->theScreens[0].bounds.top;
  131.  
  132.     PenPat(QUICKDRAWBLACK);                                            // erase the screens.
  133.     for (screenIndex = 0; screenIndex < machineInfo->numScreens; screenIndex++) 
  134.         PaintRect(&machineInfo->theScreens[screenIndex].bounds);
  135.         
  136.     PenPat(QUICKDRAWWHITE);                                            // draw the grid.
  137.     SetOrigin(-xloc, -yloc);
  138.     for (ix = 0; ix <= xsize; ix++) {
  139.         MoveTo (xc + ix * unit, yc);
  140.         Line (0, unit * ysize);
  141.     }
  142.     for (ix = 0; ix <= ysize; ix++) {
  143.         MoveTo (xc, yc + ix * unit);
  144.         Line (unit * xsize, 0);
  145.     }
  146.     SetOrigin(0, 0);
  147.     
  148.     nextDraw = 0;
  149.     
  150.     return noErr;
  151. }
  152.  
  153. // The DoRow and Play routines are Morpion's IA. They are not part of the fader's interface with DarkSide.
  154.  
  155. void DoRow (short bx, short by, short dx, short dy)
  156. {
  157.     short     i;
  158.     short     x, y;
  159.     short     nfriend, nenemy;
  160.     char    g;
  161.     
  162.     for (nfriend = nenemy = 0, x = bx, y = by, i = 5; i; x += dx, y += dy, i--) {
  163.         if ((g = Grid(x, y)) == turn)                                // count how many enemy/friendly markers
  164.             nfriend++;                                        // are present in those five squares
  165.         else if (g != empty)
  166.             nenemy++;
  167.     }
  168.     
  169.     for (x = bx, y = by, i = 5; i; x += dx, y += dy, i--)                    // increment the strategic value of the free squares
  170.         if (Grid(x, y) == empty)                                    // depending on nenemy and nfriendly
  171.             Points(x, y) += value [nfriend][nenemy];
  172. }
  173.  
  174. Point Play (void)
  175. {
  176.     short    ix, iy, npos, max, n, p;
  177.     Point    zero;
  178.     
  179.     zero.h = zero.v = 0;
  180.     for (ix = 0; ix < xsize; ix++)                                    // Only allow empty spots to be played
  181.         for (iy = 0; iy < ysize; iy++)
  182.             Points(ix, iy) = Grid(ix, iy) == empty ? 0 : -1;
  183.                 
  184.     for (ix = 0; ix < xsize-4; ix++)                                    // Walk the whole grid, giving more points to each
  185.         for (iy = 0; iy < ysize; iy++)                                // interesting spot
  186.             DoRow (ix, iy, 1, 0);
  187.     for (ix = 0; ix < xsize; ix++)
  188.         for (iy = 0; iy < ysize-4; iy++)
  189.             DoRow (ix, iy, 0, 1);
  190.     for (ix = 0; ix < xsize-4; ix++)
  191.         for (iy = 0; iy < ysize-4; iy++)
  192.             DoRow (ix, iy, 1, 1);
  193.     for (ix = 0; ix < xsize-4; ix++)
  194.         for (iy = 4; iy < ysize; iy++)
  195.             DoRow (ix, iy, 1, -1);
  196.  
  197.     for (npos = 0, max = -1, ix = 0; ix < xsize; ix++)                    // Look for the most interesting spot
  198.         for (iy = 0; iy < ysize; iy++) {
  199.             p = Points(ix, iy);
  200.             if (p > max) {                                        // If this square is more interesting than all those
  201.                 max = p; npos = 1;                                // previously visited, forget about them
  202.                 Possible (0).h = ix;
  203.                 Possible (0).v = iy;
  204.             }
  205.             else if (p == max) {                                    // If it is equally interesting than the best previously
  206.                 npos++;                                        // visited squares, add it to the array of possible
  207.                 Possible (npos-1).h = ix;                            // moves.
  208.                 Possible (npos-1).v = iy;
  209.             }
  210.         }
  211.     
  212.     if (max == 0)                                                // Nothing interesting : this is a draw
  213.         draw = true;
  214.     else if (npos) {                                                
  215.         n = Random();                                            // Randomly choose a square from the list
  216.         if (n < 0) n = -n;                                        // of best possible squares
  217.         n = n % npos;
  218.  
  219.         if (max >= value [4][0])                                    // This means we just won
  220.             winner = true;
  221.  
  222.         return Possible (n);                                        // Pick up a solution randomly from the set of
  223.     }                                                        // the best solutions
  224.     else
  225.         draw = true;                                            // The grid is full
  226.         
  227.     return zero;
  228. }
  229.  
  230. // Called regularly. This is the heart of the fader. Here we do whatever we please...
  231.  
  232. OSErr    IdleFader(MachineInfoPtr machineInfo)
  233. {
  234.     Point            location;
  235.     Rect            r;
  236.     long            finalTicks;
  237.     short        a;
  238.     
  239.     // if it isn't time to draw yet, we return -- we do this rather than delay to avoid
  240.     // locking up the machine with a function as mundane as a screensaver
  241.     if (TickCount() < nextDraw)
  242.         return(noErr);
  243.  
  244.     
  245.     location = Play();                                            // find out where we want to play
  246.  
  247.     if (!draw) {                                                // If the player actually played something
  248.         SetOrigin(-xloc, -yloc);
  249.         r.left = xc + border + unit * location.h;
  250.         r.top = yc + border + unit * location.v;
  251.         r.right = r.left + unit - 2 * border + 1;
  252.         r.bottom = r.top + unit - 2 * border + 1;
  253.         if (turn == player1)
  254.             EraseOval (&r);                                    // draw the new marker in the proper color
  255.         else {
  256.             PenPat (QUICKDRAWWHITE);
  257.             FrameOval (&r);
  258.             PenPat (QUICKDRAWBLACK);
  259.         }
  260.         
  261.         (void) PlayResourceSnd (machineInfo, 128, true);                // play sound asynchronously
  262.         for (a = 4; a; a--) {                                        // make it blink so that the user sees it
  263.             InvertRect (&r);
  264.             Delay (2, &finalTicks);
  265.         }
  266.         
  267.         SetOrigin(0, 0);
  268.     }
  269.  
  270.     // calculate the next time to draw    
  271.     nextDraw = TickCount() + 60 - machineInfo->faderSettings->theShorts[0];
  272.  
  273.     if (draw || winner) {                                            // if the game's over
  274.         Str255    resultString;
  275.         
  276.         PenPat (QUICKDRAWBLACK);
  277.         TextMode (srcXor);
  278.         r = machineInfo->theScreens[0].bounds;
  279.         MoveTo (r.left+2*unit, r.top + 2*unit);                        // print a message explaining why
  280.         if (draw)
  281.             {
  282.             GetIndString(resultString, 5000, 1);
  283.             }
  284.         else 
  285.             {
  286.             if (turn == player1)
  287.                 GetIndString(resultString, 5000, 2);
  288.             else
  289.                 GetIndString(resultString, 5000, 3);
  290.             }
  291.         DrawString(resultString);
  292.         PenNormal();
  293.         
  294.         Delay (240, &finalTicks);                                    // wait a few seconds
  295.         StartGame();                                            // and start a new game
  296.         InitializeFader(machineInfo);
  297.         return noErr;
  298.     }
  299.     
  300.  
  301.     Grid(location.h, location.v) = turn;                                // update the grid in memory
  302.     turn = (turn == player1) ? player2 : player1;                        // change turn
  303.         
  304.     return noErr;
  305. }
  306.  
  307. // Called when the fade is tearing down
  308.  
  309. OSErr    DisposeFader(MachineInfoPtr machineInfo)
  310. {
  311. #pragma unused (machineInfo)
  312.  
  313.     DisposHandle (grid);                                            // release all the memory we reserved
  314.     DisposHandle (points);
  315.     DisposHandle (possible);
  316.     return noErr;
  317. }
  318.  
  319. // Called when there is an update event for our fade window.
  320.  
  321. OSErr    UpdateFader(MachineInfoPtr machineInfo)
  322. {
  323.     InitializeFader(machineInfo);                                    // erase the screen, draw the grid
  324.     return noErr;
  325. }
  326.  
  327. // Called when there is an events in the settings dialog. itemHit will be the item the user has selected, or 0 when the dialog
  328. // is being set up. 
  329. // itemHit - itemOffset will allow you to determine which item in your dialog list this corresponds to.
  330. // If you don't wish to do any special processing of this event, simply return fnfErr and the standard effect will take place.
  331.  
  332. OSErr    HitFader(MachineInfoPtr machineInfo, DialogPtr dPtr, short itemHit, short itemOffset)
  333. {
  334. #pragma unused (machineInfo, dPtr, itemHit, itemOffset)
  335.  
  336.     return fnfErr;
  337. }
  338.